#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freetype/freetype.h"
#include "Font.h"


static TT_Error initrastermap(TT_Raster_Map *map, int width, int height, int gray_render)
{
	map->rows = height;
	map->width = width;

	if (gray_render)
	{
		map->cols = map->width;
		map->size = map->rows * map->width;
	}
	else
	{
		map->cols = (map->width + 7) / 8;	/* convert to # of bytes */
		map->size = map->rows * map->cols;	/* number of bytes in buffer */
	}

	map->flow = TT_Flow_Down;
	map->bitmap = (void *)malloc(map->size);
	if (map->bitmap == NULL)
		return 1;

	return 0;
}

static void killrastermap(TT_Raster_Map *map)
{
	if (map)
		free(map->bitmap);
}

static int getascenderanddescender(TT_Instance instance, TT_Glyph glyph, int Nglyphs, int *ascent, int *descent, int *width)
{
	int i;
	TT_Pos ascender = 0;
	TT_Pos descender = 0;
	TT_Pos maxwidth = 0;
	TT_Glyph_Metrics glyphMetrics;
	TT_UShort glyphIndex;
	TT_Error err;

	for (i = 0; i<Nglyphs; i++)
	{
		glyphIndex = (TT_UShort)i;
		err = TT_Load_Glyph(instance, glyph, glyphIndex, TTLOAD_SCALE_GLYPH | TTLOAD_HINT_GLYPH);
		if (err == 0)
		{
			TT_Get_Glyph_Metrics(glyph, &glyphMetrics);
			if (ascender < glyphMetrics.bearingY)
				ascender = glyphMetrics.bearingY;
			if (descender > glyphMetrics.bbox.yMin)
				descender = glyphMetrics.bbox.yMin;
			if (maxwidth < glyphMetrics.bbox.xMax - glyphMetrics.bbox.xMin + glyphMetrics.bearingX)
				maxwidth = glyphMetrics.bbox.xMax - glyphMetrics.bbox.xMin + glyphMetrics.bearingX;
		}
	}
	*ascent = (ascender + 31) / 64;
	*descent = (-descender + 31) / 64;
	*width = (maxwidth + 63) / 64;

	return 0;
}

static int getascenderanddescender2(TT_Instance instance, TT_Glyph glyph, TT_CharMap charmap, int *codes, int Nglyphs, int *ascent, int *descent, int *width)
{
	int i;
	TT_Pos ascender = 0;
	TT_Pos descender = 0;
	TT_Pos maxwidth = 0;
	TT_Glyph_Metrics glyphMetrics;
	TT_UShort glyphIndex;
	TT_Error err;

	for (i = 0; i<Nglyphs; i++)
	{
		glyphIndex = TT_Char_Index(charmap, codes[i]);
		err = TT_Load_Glyph(instance, glyph, glyphIndex, TTLOAD_SCALE_GLYPH | TTLOAD_HINT_GLYPH);
		if (err == 0)
		{
			TT_Get_Glyph_Metrics(glyph, &glyphMetrics);
			if (ascender < glyphMetrics.bearingY)
				ascender = glyphMetrics.bearingY;
			if (descender > glyphMetrics.bbox.yMin)
				descender = glyphMetrics.bbox.yMin;
			if (maxwidth < glyphMetrics.bbox.xMax - glyphMetrics.bbox.xMin + glyphMetrics.bearingX)
				maxwidth = glyphMetrics.bbox.xMax - glyphMetrics.bbox.xMin + glyphMetrics.bearingX;
		}
	}
	*ascent = (ascender + 31) / 64;
	*descent = (-descender + 31) / 64;
	*width = (maxwidth + 63) / 64;

	return 0;
}
/*
sort codes is ascending order (-1 = no code, at start )
*/
static int compints(const void *e1, const void *e2)
{
	return *(const int *)e1 - *(const int *)e2;
}


static int getcodes(TT_CharMap charmap, int Nglyphs, int *ret)
{
	int i, ii;

	for (i = 0; i < Nglyphs; i++)
		ret[i] = -1;
	ret[0] = 0;
	for (i = 0; i < 0xFFFF; i++)
	{
		int index = TT_Char_Index(charmap, i);
		if (index > 0 && index < Nglyphs)
			ret[index] = i;
	}
	ii = TT_Char_Index(charmap, 32);
	if (ii > 0 && ii < Nglyphs)
		ret[ii] = 32;

	/*
	for (i = 0; i<Nglyphs; i++)
	{
		for (ii = 0; ii<0xFFFF; ii++)
			if (TT_Char_Index(charmap, ii) == i)
				break;
		if (ii < 0xFFFF)
			ret[i] = ii;
		else
			ret[i] = -1;
	}
	*/
}

static int getbestcharmap(TT_Face face)
{
	int N;
	TT_UShort platformID, encodingID;
	int i;

	N = TT_Get_CharMap_Count(face);
	/* try to get a Unicode encoding 0 , 0 = Apple Unicode. 3 1 = Windows unicode */
	for (i = 0; i<N; i++)
	{
		TT_Get_CharMap_ID(face, i, &platformID, &encodingID);
		if ((platformID == 3 && encodingID == 1) ||
			(platformID == 0 && encodingID == 0))
			return i;
	}
	/* Ok, maybe its  Windows symbol font */
	for (i = 0; i<N; i++)
	{
		TT_Get_CharMap_ID(face, i, &platformID, &encodingID);
		if (platformID == 3 && encodingID == 0)
			return i;
	}

	/* just return the default Apple Roman coding */
	return 0;

}


int dumpttf(char *fname, char *name, int points, FILE *fp)
{
	int major, minor;
	TT_Engine engine;
	TT_Byte palette[5] = { 0x00, 0x40, 0x80, 0xC0, 0xFF };
	TT_Face face;
	TT_Face_Properties faceProperties;
	TT_Instance instance;
	TT_Instance_Metrics instanceMetrics;
	TT_Glyph glyph;
	TT_Glyph_Metrics glyphMetrics;
	TT_CharMap charmap;
	TT_Raster_Map map;
	TT_UShort glyphIndex;
	TT_Error err;
	int i;
	int x, y;
	int ascent, descent, maxwidth;
	int *codes;
	int *widths;
	int Nglyphs;

	TT_FreeType_Version(&major, &minor);
	err = TT_Init_FreeType(&engine);
	if (err)
	{
		fprintf(stderr, "Can't intialise free type system\n");
		return -1;
	}

	TT_Set_Raster_Gray_Palette(engine, palette);
	err = TT_Open_Face(engine, fname, &face);
	if (err)
	{
		fprintf(stderr, "Can't open ttf file %s\n", fname);
		return -1;
	}
	TT_Get_Face_Properties(face, &faceProperties);
	TT_New_Instance(face, &instance);
	TT_Set_Instance_CharSize(instance, points * 64);
	TT_Get_Instance_Metrics(instance, &instanceMetrics);
	TT_Get_CharMap(face, getbestcharmap(face), &charmap);
	codes = malloc(faceProperties.num_Glyphs * sizeof(int));
	getcodes(charmap, faceProperties.num_Glyphs, codes);
	qsort(codes, faceProperties.num_Glyphs, sizeof(int), compints);
	for (i = 0; i<faceProperties.num_Glyphs; i++)
		if (codes[i] != -1)
			break;
	Nglyphs = faceProperties.num_Glyphs - i;
	memmove(codes, codes + i, Nglyphs * sizeof(int));
	widths = malloc(Nglyphs * sizeof(int));
	TT_New_Glyph(face, &glyph);

	getascenderanddescender(instance, glyph, faceProperties.num_Glyphs, &ascent, &descent, &maxwidth);
	initrastermap(&map, (maxwidth + 3) / 4 * 4, ascent + descent, 1);

	fprintf(fp, "\nstatic unsigned char %s_bitmap[%d] =\n", name, Nglyphs* maxwidth * (ascent + descent));
	fprintf(fp, "{\n");
	for (i = 0; i<Nglyphs; i++)
	{
		memset(map.bitmap, 0, map.size);
		glyphIndex = TT_Char_Index(charmap, codes[i]);

		TT_Load_Glyph(instance, glyph, glyphIndex, TTLOAD_SCALE_GLYPH | TTLOAD_HINT_GLYPH);
		TT_Get_Glyph_Metrics(glyph, &glyphMetrics);

		widths[i] = (glyphMetrics.advance + 31) / 64;
		if (widths[i] < 0)
			widths[i] = 0;
		if (widths[i] > maxwidth)
			widths[i] = maxwidth;

		// TT_Get_Glyph_Pixmap(glyph, &map, glyphMetrics.bearingX, descent * 64);
		TT_Get_Glyph_Pixmap(glyph, &map, 0, descent * 64);
		fprintf(fp, "/* glyph %d */\n", codes[i]);
		for (y = 0; y<ascent + descent; y++)
		{
			for (x = 0; x<maxwidth; x++)
				fprintf(fp, "0x%02x, ", ((unsigned char *)map.bitmap)[y * map.width + x]);
			fprintf(fp, "\n");
		}
		fprintf(fp, "\n");
	}
	fprintf(fp, "};\n");

	fprintf(fp, "static unsigned char %s_widths[%d] =\n", name, Nglyphs);
	fprintf(fp, "{\n");
	for (i = 0; i<Nglyphs; i++)
		fprintf(fp, "%d,\n", widths[i]);
	fprintf(fp, "};\n\n");

	fprintf(fp, "static unsigned short %s_index[%d] =\n", name, Nglyphs);
	fprintf(fp, "{\n");
	for (i = 0; i<Nglyphs; i++)
		fprintf(fp, "%d,\n", codes[i]);
	fprintf(fp, "};\n\n");

	fprintf(fp, "struct bitmap_font %s_font = \n", name);
	fprintf(fp, "{\n");
	fprintf(fp, "%d, /* max width */\n", maxwidth);
	fprintf(fp, "%d, /* height */\n", ascent + descent);
	fprintf(fp, "%d, /* ascent */\n", ascent);
	fprintf(fp, "%d, /* descent */\n", descent);
	fprintf(fp, "%d, /* Nchars */\n", Nglyphs);
	fprintf(fp, "%s_widths, /* widths */\n", name);
	fprintf(fp, "%s_index, /* index */\n", name);
	fprintf(fp, "%s_bitmap /* bitmap */\n", name);
	fprintf(fp, "};\n");
	fprintf(fp, "\n");

	free(codes);
	free(widths);
	killrastermap(&map);
	TT_Done_Glyph(glyph);
	TT_Close_Face(face);
	TT_Done_FreeType(engine);

	return 0;
}

int bbx_utf8_putch(char *out, int ch);

static int getname(char *out, int maxlen, TT_Face face, TT_CharMap charmap)
{
	char *letters;
	unsigned short Nshort;
	int i;
	int j = 0;
	int glyphIndex;

	if (TT_Get_Name_String(face, 4, &letters, &Nshort) == TT_Err_Ok)
	{
		for (i = 0; i < Nshort && i <maxlen; i++)
		{
			if (letters[i])
			{
				glyphIndex = TT_Char_Index(charmap, letters[i]);
				if (glyphIndex != 0)
					out[j++] = letters[i];
			}
		}
		out[j] = 0;
	}
	if (j == 0)
	{
		for (i = 128; i < 0xFFFF; i++)
		{
			glyphIndex = TT_Char_Index(charmap, i);
			if (glyphIndex != 0)
				j += bbx_utf8_putch(out + j, i);
			if (j > maxlen-5 || j > 40)
				break;
		}
		out[j] = 0;
	}
}


struct bitmap_font *loadttffont(char *fname, int points, char *namestr, int maxlen)
{
	int major, minor;
	TT_Engine engine;
	TT_Byte palette[5] = { 0x00, 0x40, 0x80, 0xC0, 0xFF };
	TT_Face face;
	TT_Face_Properties faceProperties;
	TT_Instance instance;
	TT_Instance_Metrics instanceMetrics;
	TT_Glyph glyph;
	TT_Glyph_Metrics glyphMetrics;
	TT_CharMap charmap;
	TT_Raster_Map map;
	TT_UShort glyphIndex;
	TT_Error err = 0;
	int i ,j;
	int x, y;
	int ascent, descent, maxwidth;
	int *codes = 0;
	int *widths = 0;
	int Nglyphs;

	int tt_init_flag = 0;
	int tt_face_flag = 0;
	int tt_glyph_flag = 0;
	unsigned char *bitmap = 0;
	struct bitmap_font *font;
	char *letters;
	unsigned short Nshort;
	int Nletters;

	
	TT_FreeType_Version(&major, &minor);
	err = TT_Init_FreeType(&engine);
	if (err)
	{
		fprintf(stderr, "Can't intialise free type system\n");
		return 0;
	}
	tt_init_flag = 1;

	TT_Set_Raster_Gray_Palette(engine, palette);
	err = TT_Open_Face(engine, fname, &face);
	if (err)
	{
		fprintf(stderr, "Can't open ttf file %s\n", fname);
		return 0;
	}
	tt_face_flag = 1;
	
	TT_Get_Face_Properties(face, &faceProperties);
	TT_New_Instance(face, &instance);
	TT_Set_Instance_CharSize(instance, points * 64);
	TT_Get_Instance_Metrics(instance, &instanceMetrics);
	TT_Get_CharMap(face, getbestcharmap(face), &charmap);

	if (namestr)
	{
		Nletters = 0;
		getname(namestr, maxlen, face, charmap);
		for (i = 0; namestr[i]; i += bbx_utf8_skip(namestr + i))
			Nletters++;
		Nletters++;

		j = 0;
		codes = malloc(Nletters * sizeof(int));
		codes[0] = 0;
		for (i = 0; i < Nletters - 1; i++)
		{
			codes[i + 1] = bbx_utf8_getch(namestr + j);
			j += bbx_utf8_skip(namestr + j);
		}

		for (i = 1; i < Nletters; i++)
		{
			glyphIndex = TT_Char_Index(charmap, codes[i]);
			if (glyphIndex == 0)
				codes[i] = -1;
		}
		qsort(codes, Nletters, sizeof(int), compints);

		j = 0;
		for (i = 0; i < Nletters; i++)
			if (codes[i] != -1 && (i == 0 || codes[i] != codes[i - 1]))
				codes[j++] = codes[i];
		Nglyphs = j;
	}
	else
	{
    	codes = malloc(faceProperties.num_Glyphs * sizeof(int));
		getcodes(charmap, faceProperties.num_Glyphs, codes);
		qsort(codes, faceProperties.num_Glyphs, sizeof(int), compints);
		for (i = 0; i < faceProperties.num_Glyphs; i++)
			if (codes[i] != -1)
				break;
		Nglyphs = faceProperties.num_Glyphs - i;
		memmove(codes, codes + i, Nglyphs * sizeof(int));
		j = 0;
		for (i = 0; i < Nglyphs; i++)
		{
			codes[j++] = codes[i];
		}
		Nglyphs = j;
	}
	widths = malloc(Nglyphs * sizeof(int));
	err = TT_New_Glyph(face, &glyph);
	if (err)
		goto error_exit;
	tt_glyph_flag = 1;

	ascent = (faceProperties.horizontal->Ascender + 31)/64;
	descent = -(faceProperties.horizontal->Descender + 31)/64;
	maxwidth = (faceProperties.horizontal->xMax_Extent + 31)/64;
	

	getascenderanddescender2(instance, glyph, charmap, codes, Nglyphs, &ascent, &descent, &maxwidth);
	initrastermap(&map, (maxwidth + 3) / 4 * 4, ascent + descent, 1);

	bitmap = malloc(Nglyphs * maxwidth *(ascent + descent));

	for (i = 0; i<Nglyphs; i++)
	{
		memset(map.bitmap, 0, map.size);
		glyphIndex = TT_Char_Index(charmap, codes[i]);

		TT_Load_Glyph(instance, glyph, glyphIndex, TTLOAD_SCALE_GLYPH | TTLOAD_HINT_GLYPH);
		TT_Get_Glyph_Metrics(glyph, &glyphMetrics);

		widths[i] = (glyphMetrics.advance + 31) / 64;
		if (widths[i] < 0)
			widths[i] = 0;
		if (widths[i] > maxwidth)
			widths[i] = maxwidth;

		// TT_Get_Glyph_Pixmap(glyph, &map, glyphMetrics.bearingX, descent * 64);
		TT_Get_Glyph_Pixmap(glyph, &map, 0, descent * 64);
		
		if (codes[i] == 32)
			memset(map.bitmap, 0, map.size);

		for (y = 0; y < ascent + descent; y++)
		{
			for (x = 0; x < maxwidth; x++)
			{
				unsigned char *ptr;
				ptr = bitmap + (i * maxwidth * (ascent + descent));
				ptr[y*maxwidth + x] = ((unsigned char *)map.bitmap)[y * map.width + x];
			}
		}
		
	}
	
	font = malloc(sizeof(struct bitmap_font));
	if (!font)
		goto error_exit;
	font->widths = 0;
	font->index = 0;
	font->bitmap = 0;

	font->width = maxwidth;
	font->height = ascent + descent;
	font->ascent = ascent;
	font->descent = descent;
	font->Nchars = Nglyphs;
	font->widths = malloc(Nglyphs);
	if (!font->widths)
		goto error_exit;
	for (i = 0; i < Nglyphs; i++)
		font->widths[i] = (unsigned char)widths[i];
	font->index = malloc(Nglyphs * sizeof(short));
	if (!font->index)
		goto error_exit;
	for (i = 0; i < Nglyphs; i++)
		font->index[i] = (unsigned short) codes[i];
	font->bitmap = bitmap;
	
	free(codes);
	free(widths);

	killrastermap(&map);
	TT_Done_Glyph(glyph);
	TT_Close_Face(face);
	TT_Done_FreeType(engine);

	return font;
error_exit:
	free(codes);
	free(widths);
	free(bitmap);
	if (font)
	{
		free(font->widths);
		free(font->index);
		free(font);
	}
	killrastermap(&map);
	if (tt_glyph_flag)
	  TT_Done_Glyph(glyph);
	if (tt_face_flag)
	  TT_Close_Face(face);
	if (tt_init_flag)
	TT_Done_FreeType(engine);

}

struct bitmap_font *loadttffontsample(char *path, int points, char *textout, int N)
{
	return loadttffont(path, points, textout, N);
}

struct bitmap_font *loadttffontfull(char *path, int points)
{
	return loadttffont(path, points, 0, 0);
}

static void usage(void)
{
	printf("ttf2c - converts a ttf file to an ANSI C bitmapped font\n");
	printf("Usage: <font.ttf> <name> <points>\n");
	printf("Creates C file on stdout\n");
	exit(EXIT_FAILURE);
}

int ttf2cmain(int argc, char **argv)
{
	if (argc != 4)
		usage();
	printf("#include \"font.h\"\n");
	dumpttf(argv[1], argv[2], atoi(argv[3]), stdout);

	return 0;
}
